#include "bookmarksdialog.h"
#include "ui_bookmarksdialog.h"

BookmarksDialog::BookmarksDialog(QWidget *parent, DebugConsole *debugconsole, const QString &dbPath) :
    QDialog(parent),
    ui(new Ui::BookmarksDialog)
{
    ui->setupUi(this);
    console = debugconsole;

    db = QSqlDatabase::addDatabase("QSQLITE");

    // QString filepath = QFileInfo(settings.fileName()).path();
    QFileInfo dbname(QDir(dbPath), "bookmarks.sqlite");
    db.setDatabaseName(dbname.filePath());
    if (!db.open()) console->error("Failed to open bookmarks.");

    // create bookmarks table
    QSqlQuery bookmarksQuery(
                    "CREATE TABLE IF NOT EXISTS bookmarks ("
                    "path TEXT NOT NULL, filename TEXT NOT NULL, place TEXT NOT NULL, timestamp NUMERIC NOT NULL)"
                );
    QSqlQuery bookmarksIndexQuery("CREATE UNIQUE INDEX IF NOT EXISTS bookmarkspfp ON bookmarks (path, filename, place)");

    // create last read table
    QSqlQuery lastreadQuery(
                    "CREATE TABLE IF NOT EXISTS lastread ("
                    "path TEXT NOT NULL, filename TEXT NOT NULL, place TEXT NOT NULL, timestamp NUMERIC NOT NULL)"
                );
    QSqlQuery lasteadIndexQuery("CREATE UNIQUE INDEX IF NOT EXISTS lastreadpf on lastread (path, filename)");

    bookmarksTableModel = new YasaiSqlTableModel(this, db);
    bookmarksTableModel->setTable("bookmarks");
    bookmarksTableModel->setHeaderData(0, Qt::Horizontal, "Path");
    bookmarksTableModel->setHeaderData(1, Qt::Horizontal, "Filename");
    bookmarksTableModel->setHeaderData(2, Qt::Horizontal, "Place");
    bookmarksTableModel->setHeaderData(3, Qt::Horizontal, "Timestamp");
    ui->bookmarkTableView->setModel(bookmarksTableModel);
    ui->bookmarkTableView->setSortingEnabled(true);

    lastreadTableModel = new YasaiSqlTableModel(this, db);
    lastreadTableModel->setTable("lastread");
    lastreadTableModel->setHeaderData(0, Qt::Horizontal, "Path");
    lastreadTableModel->setHeaderData(1, Qt::Horizontal, "Filename");
    lastreadTableModel->setHeaderData(2, Qt::Horizontal, "Place");
    lastreadTableModel->setHeaderData(3, Qt::Horizontal, "Timestamp");
    ui->lastreadTableView->setModel(lastreadTableModel);
    ui->lastreadTableView->setSortingEnabled(true);
}

void BookmarksDialog::showEvent(QShowEvent *)
{
    bookmarksTableModel->select();
    ui->bookmarkTableView->resizeColumnToContents(1);
    ui->bookmarkTableView->resizeColumnToContents(2);
    ui->bookmarkTableView->resizeColumnToContents(3);
    ui->bookmarkTableView->setEditTriggers(QAbstractItemView::NoEditTriggers);

    lastreadTableModel->select();
    ui->lastreadTableView->resizeColumnToContents(1);
    ui->lastreadTableView->resizeColumnToContents(2);
    ui->lastreadTableView->resizeColumnToContents(3);
    ui->lastreadTableView->setEditTriggers(QAbstractItemView::NoEditTriggers);
}

bool BookmarksDialog::saveBookmark(const QFileInfo &fileInfo, const QString &place)
{
    QSqlQuery query;
    query.prepare("INSERT OR REPLACE INTO bookmarks (path, filename, place, timestamp) "
                  "VALUES (:path, :filename, :place, :timestamp)");
    query.bindValue(":path", fileInfo.path());
    query.bindValue(":filename", fileInfo.fileName());
    query.bindValue(":place", place);
    query.bindValue(":timestamp", QDateTime::currentDateTime().toTime_t());
    if (!query.exec())
    {
        console->error(db.lastError().text());
        return false;
    }
    bookmarksTableModel->select();
    return true;
}

bool BookmarksDialog::saveLastRead(const QFileInfo &fileInfo, const QString &place)
{
    QSqlQuery query;
    query.prepare("INSERT OR REPLACE INTO lastread (path, filename, place, timestamp) "
                  "VALUES (:path, :filename, :place, :timestamp)");
    query.bindValue(":path", fileInfo.path());
    query.bindValue(":filename", fileInfo.fileName());
    query.bindValue(":place", place);
    query.bindValue(":timestamp", QDateTime::currentDateTime().toTime_t());
    if (!query.exec())
    {
        console->error(db.lastError().text());
        return false;
    }
    lastreadTableModel->select();
    return true;
}

BookmarksDialog::~BookmarksDialog()
{
    delete ui;
    db.close();
}

void BookmarksDialog::on_lastreadTableView_activated(const QModelIndex &index)
{
    QSqlRecord record = lastreadTableModel->record(index.row());
    QString path = record.field("path").value().toString();
    QString filename = record.field("filename").value().toString();
    QString place = record.field("place").value().toString();

    if (QFileInfo(QDir(path), filename).isFile())
    {
        emit loadBookmark(QFileInfo(QDir(path), filename), place);
        this->hide();
        return;
    }
}

void BookmarksDialog::on_bookmarkTableView_activated(const QModelIndex &index)
{
    QSqlRecord record = bookmarksTableModel->record(index.row());
    QString path = record.field("path").value().toString();
    QString filename = record.field("filename").value().toString();
    QString place = record.field("place").value().toString();

    if (QFileInfo(QDir(path), filename).isFile())
    {
        emit loadBookmark(QFileInfo(QDir(path), filename), place);
        this->hide();
        return;
    }

    // otherwise ask user to update bookmark
    QMessageBox msgBox;
    msgBox.setText(tr("The bookmark refers to a file that no longer exists."));
    msgBox.setInformativeText(tr("Do you wish to update the bookmark, or remove it?"));
    QPushButton *update = msgBox.addButton(tr("Update"), QMessageBox::AcceptRole);
    QPushButton *remove = msgBox.addButton(tr("Remove"), QMessageBox::DestructiveRole);
    QPushButton *removeAll = msgBox.addButton(tr("Remove all"), QMessageBox::DestructiveRole);
    msgBox.addButton(QMessageBox::Abort);
    msgBox.exec();
    if (msgBox.clickedButton() == update)
    {
        QString newFile = QFileDialog::getOpenFileName(this, tr("New location of file"));
        QSqlQuery q;
        q.prepare("UPDATE bookmarks "
                  "SET path=:newPath, filename=:newFilename "
                  "WHERE path=:oldPath AND filename=:oldFilename");
        q.bindValue(":oldPath", path);
        q.bindValue(":oldFilename", filename);
        q.bindValue(":newPath", QFileInfo(newFile).path());
        q.bindValue(":newFilename", QFileInfo(newFile).fileName());

        if (!q.exec())
        {
            console->error(tr("Unable to update bookmarks with new location."));
            console->error(q.lastError().text());
        }
    }
    if (msgBox.clickedButton() == remove)
    {
        bookmarksTableModel->removeRow(index.row());
    }
    if (msgBox.clickedButton() == removeAll)
    {
        QSqlQuery removeQuery;
        removeQuery.prepare("DELETE FROM bookmarks WHERE "
                            "path=:path AND filename=:filename");
        removeQuery.bindValue(":path", path);
        removeQuery.bindValue(":filename", filename);
        if (!removeQuery.exec()) console->error(tr("Unable to remove all %1").arg(filename));
    }
    bookmarksTableModel->select();
}

void BookmarksDialog::on_loadBookmark_clicked()
{
    on_bookmarkTableView_activated(ui->bookmarkTableView->currentIndex());
}

void BookmarksDialog::on_actionRemoveInvalidBookmarks_triggered()
{
    QSqlQuery q("SELECT DISTINCT path, filename FROM bookmarks");
    while (q.next())
    {
        QString path = q.value(0).toString();
        QString filename = q.value(1).toString();
        QFileInfo fileInfo = QFileInfo(QDir(path), filename);
        if (!fileInfo.isFile())
        {
            QSqlQuery removeQuery;
            removeQuery.prepare("DELETE FROM bookmarks WHERE "
                                "path=:path AND filename=:filename");
            removeQuery.bindValue(":path", path);
            removeQuery.bindValue(":filename", filename);
            if (!removeQuery.exec()) console->error(tr("Unable to remove %1").arg(fileInfo.filePath()));
        }
    }

    QSqlQuery vacuum("VACUUM");
    bookmarksTableModel->select();
}

void BookmarksDialog::contextMenuEvent(QContextMenuEvent *event)
{
    QMenu menu(this);
    menu.addAction(ui->actionRemoveBookmark);
    menu.addAction(ui->actionRemoveSelectedType);
    menu.addAction(ui->actionRemoveInvalidBookmarks);
    menu.addAction(ui->actionVacuum);
    menu.exec(event->globalPos());
}

void BookmarksDialog::on_removeBookmark_clicked()
{
    this->on_actionRemoveBookmark_triggered();
}

void BookmarksDialog::on_actionVacuum_triggered()
{
    QSqlQuery vacuum("VACUUM");
}

void BookmarksDialog::on_actionRemoveBookmark_triggered()
{
    if (ui->bookmarksTabWidget->currentIndex() == ui->bookmarksTabWidget->indexOf(ui->bookmarksTab))
    {
        bookmarksTableModel->removeRow(ui->bookmarkTableView->currentIndex().row());
        bookmarksTableModel->select();
    }
    if (ui->bookmarksTabWidget->currentIndex() == ui->bookmarksTabWidget->indexOf(ui->lastreadTab))
    {
        lastreadTableModel->removeRow(ui->lastreadTableView->currentIndex().row());
        lastreadTableModel->select();
    }
}

void BookmarksDialog::on_actionRemoveSelectedType_triggered()
{
    if (ui->bookmarksTabWidget->currentIndex() == ui->bookmarksTabWidget->indexOf(ui->bookmarksTab))
    {
        QString value = bookmarksTableModel->data(ui->bookmarkTableView->currentIndex()).toString();
        int column = ui->bookmarkTableView->selectionModel()->selectedIndexes().at(0).column();
        QString fieldname = bookmarksTableModel->headerData(column, Qt::Horizontal).toString().toLower();

        // cannot bind column names using qsqlquery
        QString queryString = QString("DELETE FROM bookmarks WHERE %1=:value").arg(fieldname);
        QSqlQuery q;
        q.prepare(queryString);
        q.bindValue(":value", value);

        QString question = QString(tr("Remove all bookmarks referring to %1?")).arg(value);
        if (QMessageBox::question(this, "Yasai", question) == QMessageBox::Yes)
        {
            if (!q.exec()) console->log(q.lastError().text());
            bookmarksTableModel->select();
        }
    }
}

void BookmarksDialog::on_MenuBookmarksListAboutToShow()
{
    QMenu *menuBookmarksList = qobject_cast<QMenu *>(sender());
    this->populateMenu(menuBookmarksList, bookmarksTableModel);
}

void BookmarksDialog::on_MenuLastReadListAboutToShow()
{
    QMenu *menuBookmarksList = qobject_cast<QMenu *>(sender());
    this->populateMenu(menuBookmarksList, lastreadTableModel);
}

void BookmarksDialog::populateMenu(QMenu *menu, YasaiSqlTableModel *model)
{
    menu->clear();

    model->select();
    for (int i=0; i<model->rowCount(); ++i)
    {
        QSqlRecord record = model->record(i);
        QString path = record.field("path").value().toString();
        QString filename = record.field("filename").value().toString();
        QString place = record.field("place").value().toString();

        QAction* action = new QAction(menu);
        action->setText(filename + ": " + place);
        action->setIconVisibleInMenu(false);
        action->setProperty("path", path);
        action->setProperty("filename", filename);
        action->setProperty("place", place);
        QObject::connect(action, SIGNAL(triggered()), this, SLOT(on_actionBookmarksListTriggered()));

        menu->addAction(action);
    }
}

void BookmarksDialog::on_actionBookmarksListTriggered()
{
    QAction *action = qobject_cast<QAction*>(sender());
    const QString& path = action->property("path").toString();
    const QString& filename = action->property("filename").toString();
    const QString& place = action->property("place").toString();

    QFileInfo fileinfo(QDir(path), filename);
    if (fileinfo.isFile()) emit loadBookmark(fileinfo, place);
}
